package com.dhcc.sds.util;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/**
 *
 * @author syj
 * @date 2021-05-28 16:06:41
 *
 * */
public class IdentityCodeUtil {

    /** 台湾身份首字母对应数字 */
    public static Map<String, Integer> twFirstCode = new HashMap<String, Integer>();
    /** 香港身份首字母对应数字 */
    public static Map<String, Integer> hkFirstCode = new HashMap<String, Integer>();
    static {
        twFirstCode.put("A", 10);
        twFirstCode.put("B", 11);
        twFirstCode.put("C", 12);
        twFirstCode.put("D", 13);
        twFirstCode.put("E", 14);
        twFirstCode.put("F", 15);
        twFirstCode.put("G", 16);
        twFirstCode.put("H", 17);
        twFirstCode.put("J", 18);
        twFirstCode.put("K", 19);
        twFirstCode.put("L", 20);
        twFirstCode.put("M", 21);
        twFirstCode.put("N", 22);
        twFirstCode.put("P", 23);
        twFirstCode.put("Q", 24);
        twFirstCode.put("R", 25);
        twFirstCode.put("S", 26);
        twFirstCode.put("T", 27);
        twFirstCode.put("U", 28);
        twFirstCode.put("V", 29);
        twFirstCode.put("X", 30);
        twFirstCode.put("Y", 31);
        twFirstCode.put("W", 32);
        twFirstCode.put("Z", 33);
        twFirstCode.put("I", 34);
        twFirstCode.put("O", 35);
        hkFirstCode.put("A", 1);
        hkFirstCode.put("B", 2);
        hkFirstCode.put("C", 3);
        hkFirstCode.put("R", 18);
        hkFirstCode.put("U", 21);
        hkFirstCode.put("Z", 26);
        hkFirstCode.put("X", 24);
        hkFirstCode.put("W", 23);
        hkFirstCode.put("O", 15);
        hkFirstCode.put("N", 14);
    }

    public static boolean isIDCardNo(String id) {
        if (id == null)
            return false;
        id = id.toUpperCase();
        if (id.length() != 15 && id.length() != 18  && id.length() != 8 && id.length() != 10) {
            return false;
        }
        if (id.length() == 15 || id.length() == 18) {
            return validateIdCard18(id);
        } else {
            if (id.matches("^[a-zA-Z][0-9]{9}")) {
                return validateTWCard(id);
            } else if (id.matches("^[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?$")) {
                return validateHKCard(id);
            } else if (id.matches("^[1|5|7][0-9]{6}\\(?[0-9A-Z]\\)?$")) {
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * 验证18位身份编码是否合法
     *
     * @param idCard
     *            身份编码
     * @return 是否合法
     */
    public static boolean validateIdCard18(String idCard) {
        if (idCard == null)
            return false;
        idCard = idCard.toUpperCase();
        if (idCard.length() != 15 && idCard.length() != 18) {
            return false;
        }
        int y = 0, m = 0, d = 0;
        if (idCard.length() == 15) {
            y = Integer.parseInt("19" + idCard.substring(6, 8), 10);
            m = Integer.parseInt(idCard.substring(8, 10), 10);
            d = Integer.parseInt(idCard.substring(10, 12), 10);
        } else if (idCard.length() == 18) {
            if (idCard.indexOf("X") >= 0 && idCard.indexOf("X") != 17) {
                return false;
            }
            char verifyBit = 0;
            int sum = (idCard.charAt(0) - '0') * 7 + (idCard.charAt(1) - '0') * 9 + (idCard.charAt(2) - '0') * 10
                    + (idCard.charAt(3) - '0') * 5 + (idCard.charAt(4) - '0') * 8 + (idCard.charAt(5) - '0') * 4
                    + (idCard.charAt(6) - '0') * 2 + (idCard.charAt(7) - '0') * 1 + (idCard.charAt(8) - '0') * 6
                    + (idCard.charAt(9) - '0') * 3 + (idCard.charAt(10) - '0') * 7 + (idCard.charAt(11) - '0') * 9
                    + (idCard.charAt(12) - '0') * 10 + (idCard.charAt(13) - '0') * 5 + (idCard.charAt(14) - '0') * 8
                    + (idCard.charAt(15) - '0') * 4 + (idCard.charAt(16) - '0') * 2;
            sum = sum % 11;
            switch (sum) {
                case 0:
                    verifyBit = '1';
                    break;
                case 1:
                    verifyBit = '0';
                    break;
                case 2:
                    verifyBit = 'X';
                    break;
                case 3:
                    verifyBit = '9';
                    break;
                case 4:
                    verifyBit = '8';
                    break;
                case 5:
                    verifyBit = '7';
                    break;
                case 6:
                    verifyBit = '6';
                    break;
                case 7:
                    verifyBit = '5';
                    break;
                case 8:
                    verifyBit = '4';
                    break;
                case 9:
                    verifyBit = '3';
                    break;
                case 10:
                    verifyBit = '2';
                    break;

            }

            if (idCard.charAt(17) != verifyBit) {
                return false;
            }
            y = Integer.parseInt(idCard.substring(6, 10), 10);
            m = Integer.parseInt(idCard.substring(10, 12), 10);
            d = Integer.parseInt(idCard.substring(12, 14), 10);
        }

        int currentY = Calendar.getInstance().get(Calendar.YEAR);

        /*
         * if(isGecko){ currentY += 1900; }
         */
        if (y > currentY || y < 1870) {
            return false;
        }
        if (m < 1 || m > 12) {
            return false;
        }
        if (d < 1 || d > 31) {
            return false;
        }
        return true;
    }

    /**
     * 验证台湾身份证号码
     * 共10位
     * 计算校验码的步骤：1、英文转成数字，其个位数字*9+十位数
     *         2、中间八位数字，各数字从左到右依次乘以8、7、6、5....2、1
     *         3、计算步骤1和2的和sum
     *         4、计算10-sum%10 ,其结果就是检查码，若余数为0，则检查码就是0
     * @param idCard
     *            身份证号码
     * @return 验证码是否符合
     */
    private static boolean validateTWCard(String idCard){
        //第一位是字母（表示地区代码），通过map找到对应的数字代码
        String start = idCard.substring(0, 1);
        //第二位数字表示性别，男生为1，女生为2
        String mid = idCard.substring(1, 9);
        //最后一位是检查码,由第一位和8个数字的组合计算得到校验和
        String end = idCard.substring(9, 10);
        Integer iStart = twFirstCode.get(start);

        Integer sum = iStart / 10 + (iStart % 10) * 9;
        char[] chars = mid.toCharArray();
        Integer iflag = 8;
        for (char c : chars) {
            sum = sum + Integer.valueOf(c + "") * iflag;
            iflag--;
        }
        return (sum % 10 == 0 ? 0 : (10 - sum % 10)) == Integer.valueOf(end) ? true : false;
    }

    /**
     * 验证香港身份证号码(存在Bug，部份特殊身份证无法检查)
     *
     * 如：身份证号码是：C668668（9）
     * 香港身份证号码由三部分组成：一个英文字母；6个数字；括号及0-9中的任一个数字，或者字母A。
     * 括号中的数字或字母A，是校验码，用于检验括号前面的号码的逻辑正确性。
     *
     * 身份证前2位为英文字符，如果只出现一个英文字符则表示第一位是空格，对应数字58, 前2位英文字符A-Z分别对应数字10-35
     * 最后一位校验码为0-9的数字加上字符"A"，"A"代表10
     * 将身份证号码全部转换为数字，分别对应乘9、8...1相加的总和，整除11则证件号码有效
     *
     * @param idCard
     *            身份证号码
     * @return 验证码是否符合
     */
    public static boolean validateHKCard(String idCard) {
        String card = idCard.replaceAll("[\\(|\\)]", "");
        Integer sum = 0;
        if (card.length() == 9) {
            sum = (Integer.valueOf(card.substring(0, 1).toUpperCase().toCharArray()[0]) - 55) * 9
                    + (Integer.valueOf(card.substring(1, 2).toUpperCase().toCharArray()[0]) - 55) * 8;
            card = card.substring(1, 9);
        } else {
            sum = 522 + (Integer.valueOf(card.substring(0, 1).toUpperCase().toCharArray()[0]) - 55) * 8;
        }
        String mid = card.substring(1, 7);
        String end = card.substring(7, 8);
        char[] chars = mid.toCharArray();
        Integer iflag = 7;
        for (char c : chars) {
            sum = sum + Integer.valueOf(c + "") * iflag;
            iflag--;
        }
        if (end.toUpperCase().equals("A")) {
            sum = sum + 10;
        } else {
            sum = sum + Integer.valueOf(end);
        }
        return (sum % 11 == 0) ? true : false;
    }

    public static void main(String[] args) {
        System.out.println("请输入身份证号：");
        Scanner scanner = new Scanner(System.in);
        System.out.println(isIDCardNo(scanner.next()));
    }
}